package com.qire.other.advert.impl;

import android.app.Activity;
import android.app.Application;
import android.content.Context;
import android.graphics.Color;
import android.os.Bundle;
import android.util.Log;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;

import com.bytedance.sdk.openadsdk.AdSlot;
import com.bytedance.sdk.openadsdk.FilterWord;
import com.bytedance.sdk.openadsdk.TTAdConfig;
import com.bytedance.sdk.openadsdk.TTAdConstant;
import com.bytedance.sdk.openadsdk.TTAdDislike;
import com.bytedance.sdk.openadsdk.TTAdManager;
import com.bytedance.sdk.openadsdk.TTAdNative;
import com.bytedance.sdk.openadsdk.TTAdSdk;
import com.bytedance.sdk.openadsdk.TTDislikeDialogAbstract;
import com.bytedance.sdk.openadsdk.TTNativeExpressAd;
import com.bytedance.sdk.openadsdk.TTRewardVideoAd;
import com.bytedance.sdk.openadsdk.TTSplashAd;
import com.bytedance.sdk.openadsdk.dislike.TTDislikeListView;
import com.qire.other.R;
import com.qire.other.advert.Advert;
import com.qire.other.advert.AdvertDockingDevice;

import java.util.ArrayList;
import java.util.List;

import androidx.annotation.MainThread;
import androidx.annotation.NonNull;
import androidx.fragment.app.FragmentActivity;

/**
 * 穿山甲的广告
 */
public class PangolinAdvert extends Advert {

    private final String LogTag = "PangolinAdvert";

    private static final String APP_ID = "5064041";
    private static final String APP_NAME = "奇热漫画";

    private static volatile boolean isInit = false;

    public PangolinAdvert(String advertCode) {
        super(advertCode);
    }

    /**
     * 穿山甲初始化配置
     * @return
     */
    private static TTAdConfig buildConfig() {
        return new TTAdConfig.Builder()
                .debug(true)                                        // 可选参数：是否打开debug调试信息输出：true打开、false关闭。默认false关闭
                .appId(APP_ID)                                      // 必选参数：穿山甲平台申请的APP_ID
                .appName(APP_NAME)                                  // 必选参数：APP应用名
                .asyncInit(true)                                    // 可选参数: 是否异步初始化sdk,设置为true可以减少SDK初始化耗时
                .titleBarTheme(TTAdConstant.TITLE_BAR_THEME_DARK)   // 可选参数：设置落地页主题，默认为TITLE_BAR_THEME_LIGHT，目前替换使用：TITLE_BAR_THEME_DARK
                .useTextureView(true)                               // 可选参数：使用TextureView控件播放视频,默认为false不使用，如果使用的话默认使用的是SurfaceView，当有SurfaceView冲突的场景，可以使用TextureView
                .allowShowNotify(true)                              // 可选参数：是否允许sdk展示通知栏提示，true允许、false禁止。默认为true允许
                .supportMultiProcess(false)                         // 可选参数：设置是否支持多进程：true支持、false不支持。默认为false不支持
//                .httpStack(new MyOkStack3())                        // 可选参数：设置外部网络请求，默认为urlconnection，demo中给出了okhttp3版本的样例，其余请自行开发或者咨询工作人员。
                // 允许直接下载的网络状态集合，不调用则所有网络下均无下载确认弹窗，调用不传参数则所有网络均有下载确认弹窗
                .directDownloadNetworkType(TTAdConstant.NETWORK_STATE_WIFI, TTAdConstant.NETWORK_STATE_3G)
//                .customController() //可选参数：可以设置隐私信息控制开关, 可阅读穿山甲集成文档里的SDK初始化找答案
                .allowShowPageWhenScreenLock(true)                  // 是否在锁屏场景支持展示广告落地页,已过期，可以删除，新版SDK使用规范里并未填写，目前保留，2个版本后删除（当前2.5.0）
                .needClearTaskReset()
                .build();
    }

    /**
     * 初始化穿山甲 SDK <br />
     * 开发者需要在 {@link Application#onCreate()} 方法中调用以下代码来初始化穿山甲sdk。<br />
     * 重点：穿山甲SDK需要在主线程中初始化
     * 重点：目前SDK已支持多进程，如果明确某个进程不会使用到广告SDK，可以只针对特定进程初始化广告sdk
     * @param context
     */
    public synchronized static void init(Context context) {
        if(!isInit) {
            isInit = true;
            TTAdSdk.init(context, buildConfig());
        }
    }

    /**
     * 如果为了获取更精准的广告投放需要开放一些采集数据权限（如位置等信息），可以在合适时机调用这个来获得授权，
     * 但是在 APP启动申明中需要描述具体权限用途为推送更精准广告，该授权不是必须的，可以不使用。
     * @param context TTAdManager接口中的方法，context可以是 Activity 或 Application
     */
    public static void requestPermission(Context context) {
        TTAdSdk.getAdManager().requestPermissionIfNecessary(context);
    }

    private TTAdManager adManager() {
        if (!isInit) {
            throw new RuntimeException("TTAdSdk is not init, please check.");
        }
        return TTAdSdk.getAdManager();
    }

    /**
     * 设置广告的不喜欢，开发者可自定义样式
     * @param ad
     * @param customStyle 是否自定义样式，true:样式自定义
     */
    private void bindDislike(AdvertDockingDevice advertDockingDevice, TTNativeExpressAd ad, boolean customStyle) {
        if (customStyle) {
            //使用自定义样式
            List<FilterWord> words = ad.getFilterWords();
            if (words == null || words.isEmpty()) {
                return;
            }

            final DislikeDialog dislikeDialog = new DislikeDialog(advertDockingDevice.getContext(), words);
            dislikeDialog.setOnDislikeItemClick(filterWord -> {
                // 用户选择不喜欢原因后，移除广告展示
                advertDockingDevice.removeAll();
            });
            ad.setDislikeDialog(dislikeDialog);
            return;
        }
        //使用默认个性化模板中默认dislike弹出样式
        ad.setDislikeCallback((Activity) advertDockingDevice.getContext(), new TTAdDislike.DislikeInteractionCallback() {
            @Override
            public void onSelected(int position, String value) {
                // 用户选择不喜欢原因后，移除广告展示
                advertDockingDevice.removeAll();
            }

            @Override
            public void onCancel() {
                // 点击取消
            }

            @Override
            public void onRefuse() {
                // 举报拒绝
            }
        });
    }

    @Override
    public void playVideo(final FragmentActivity activity) {
        TTAdNative mTTAdNative = adManager().createAdNative(activity);

        /**
         * 备注：模板渲染激励视频 和 SDK渲染激励视频 的广告请求方式上没有差异，仅是请求参数上的差异。
         * 模板渲染的激励视频请求方法中需要设置 setExpressViewAcceptedSize 参数,设置的值大于0即可
         * */
        // 模版渲染请求AdSlot ： 默认提供
        AdSlot adSlot = new AdSlot.Builder()
                .setCodeId(advertCode)              // 平台创建的代码位ID 以9开头9位数字
                .setUserID("user123")           // 用户id,应用侧账户 保证唯一性即可（选填）
                .setRewardName("金币")           // 奖励名称（选填） 与创建代码位填写的保持一致 。3300及以上版本中废弃
                .setRewardAmount(3)             // 奖励数量（选填） 与创建代码位填写的保持一致。3300及以上版本中废弃
                .setMediaExtra("media_extra")   // 附加参数, 用户透传信息（选填）
                // 必填参数，期望视频的播放方向：TTAdConstant.HORIZONTAL 或 TTAdConstant.VERTICAL
                .setOrientation(TTAdConstant.VERTICAL)
                // 模板广告需要设置期望个性化模板广告的大小,单位dp,激励视频场景，只要设置的值大于0即可，且仅是模板渲染的代码位ID使用，非模板渲染代码位切勿使用
                .setExpressViewAcceptedSize(500, 500)
                .build();

        mTTAdNative.loadRewardVideoAd(adSlot, new TTAdNative.RewardVideoAdListener() {
            // 是否缓存完毕
            private boolean mIsLoaded = false;
            private TTRewardVideoAd videoAd;
            private FragmentActivity owner = activity;

            private void release() {
                if(owner.isFinishing() || (videoAd != null && mIsLoaded)) {
                    videoAd = null;
                    owner = null;
                    mIsLoaded = false;
                }
            }

            private boolean showVideoAd() {
                try {
                    if (owner.isFinishing()) {
                        owner = null;
                        return false;
                    }

                    if (videoAd != null && mIsLoaded) {
                        //展示广告，并传入广告展示的场景
                        onStart();
                        videoAd.showRewardVideoAd(owner, TTAdConstant.RitScenes.CUSTOMIZE_SCENES, "scenes_summer");
                        return true;
                    } else {
                        return false;
                    }
                } finally {
                    release();
                }
            }

            // 请求广告失败
            @Override
            public void onError(int code, String message) {
                Log.d(LogTag, String.format("激励视频广告加载失败，错误代码: %s ，错误说明：%s", code, message));
                PangolinAdvert.this.onError();
            }

            // 视频广告加载后，视频资源缓存到本地的回调，在此回调后，播放本地视频，流畅不阻塞。
            @Override
            public void onRewardVideoCached() {
                // 注：为保证广告视频播放的流畅建议在onRewardVideoCached回调加载完毕后，
                // 再在主线程中调用showRewardVideoAd方法进行广告的展示。 展示广告完毕之后及时把广告对象置为null
                //开发者做一个标识
                mIsLoaded = true;
                showVideoAd();
            }

            // 视频广告的素材加载完毕，比如视频url等，在此回调后，可以播放在线视频，网络不好可能出现加载缓冲，影响体验。
            @Override
            public void onRewardVideoAdLoad(TTRewardVideoAd ad) {
                videoAd = ad;
                if(videoAd != null) {
                    videoAd.setRewardAdInteractionListener(new TTRewardVideoAd.RewardAdInteractionListener() {
                        private boolean isSkip = false;
                        //广告的下载bar点击回调
                        @Override
                        public void onAdVideoBarClick() { }
                        //视频广告关闭回调
                        @Override
                        public void onAdClose() {
                            if(isSkip) {
                                onSkipped();
                            } else {
                                onClosed();
                            }
                        }
                        //视频播放完成回调
                        @Override
                        public void onVideoComplete() {
                            onPlayComplete();
                        }
                        //视频广告播放错误回调
                        @Override
                        public void onVideoError() {
                            PangolinAdvert.this.onError();
                        }

                        //视频播放完成后，奖励验证回调，rewardVerify：是否有效，rewardAmount：奖励梳理，rewardName：奖励名称，code：错误码，msg：错误信息
                        @Override
                        public void onRewardVerify(boolean rewardVerify, int rewardAmount, String rewardName,int code,String msg) { }
                        //视频广告跳过回调
                        @Override
                        public void onSkippedVideo() {
                            this.isSkip = true;
                        }
                        //视频广告展示回调
                        @Override
                        public void onAdShow() { }
                    });
                }
                showVideoAd();
            }
        });
    }

    @Override
    public void injectionBanner(AdvertDockingDevice advertDockingDevice, int layoutRid) {
        TTAdNative mTTAdNative = adManager().createAdNative(advertDockingDevice.getContext());
        /**
         * 注意：
         * 1.模板广告请求方法需要开发者设置setExpressViewAcceptedSize参数，单位dp。
         * 2. 期望个性化模板尺寸的参数设置中，个性化模板Banner广告高度不能设置为0
         * 3. setExpressViewAcceptedSize参数请按照平台勾选的比例去进行请求。平台现支持8种尺寸，
         * 请求比例与平台创建代码位ID时选择的尺寸不一致时，会出现展示效果的badcase。 因此请求比例应与平台勾选的保持一致
         */

        AdSlot adSlot = new AdSlot.Builder()
                .setCodeId(advertCode) //广告位id
                .setSupportDeepLink(true)
                .setAdCount(1) //请求广告数量为1到3条
                .setExpressViewAcceptedSize(advertWidth, advertHeight) //期望模板广告view的size,单位dp
                .build();

        /**
         * 注意：代码位在创建的时候需要选择是否支持轮播
         * 若选择轮播则需要在onNativeExpressAdLoad回调里面调用TTNativeExpressAd.setSlideIntervalTime(30 * 1000);设置轮播时间
         * 设置Banner广告轮播的间隔时间，如果不设置则默认不开启轮播 Banner广告轮播的间隔时间，允许值在30 * 1000 ~ 120 *1000之间
         */
        mTTAdNative.loadBannerExpressAd(adSlot, new TTAdNative.NativeExpressAdListener() {
            //请求失败回调
            @Override
            public void onError(int code, String message) {
                Log.d(LogTag, String.format("模板banner广告加载失败，错误代码: %s ，错误说明：%s", code, message));
            }

            //请求成功回调
            @Override
            public void onNativeExpressAdLoad(List<TTNativeExpressAd> adList) {
                if(adList != null) {
                    for(TTNativeExpressAd ad : adList) {
                        ad.setExpressInteractionListener(new TTNativeExpressAd.ExpressAdInteractionListener() {
                            //广告点击回调
                            @Override
                            public void onAdClicked(View view, int type) { }

                            //广告展示回调
                            @Override
                            public void onAdShow(View view, int type) { }

                            //广告渲染失败回调
                            @Override
                            public void onRenderFail(View view, String msg, int code) {
                                Log.d(LogTag, String.format("模板banner广告渲染失败，错误代码: %s ，错误说明：%s", code, msg));
                            }
                            //广告渲染成功回调
                            @Override
                            public void onRenderSuccess(View view, float width, float height) {
                                advertDockingDevice.insert(view);
                            }
                        });
                        ad.render();
                        // 不喜欢样式设置
                        bindDislike(advertDockingDevice, ad, false);
                    }
                }
            }
        });

    }

    @Override
    public void injectionInfoFlow(AdvertDockingDevice advertDockingDevice) {
        //创建TTAdNative对象，createAdNative(Context context) context需要传入Activity对象
        TTAdNative mTTAdNative = adManager().createAdNative(advertDockingDevice.getContext());

        AdSlot adSlot = new AdSlot.Builder()
                .setCodeId(advertCode)              // 广告位id
                .setSupportDeepLink(true)
                .setAdCount(1)                  // 请求广告数量为1到3条
                .setExpressViewAcceptedSize(advertWidth,0) // 期望模板广告view的size,单位dp
                .build();

        mTTAdNative.loadNativeExpressAd(adSlot, new TTAdNative.NativeExpressAdListener() {
            //广告请求失败
            @Override
            public void onError(int code, String message) {
                Log.d(LogTag, String.format("信息流广告加载失败，错误代码: %s ，错误说明：%s", code, message));
            }

            //广告请求成功
            @Override
            public void onNativeExpressAdLoad(List<TTNativeExpressAd> adList) {
                if(adList != null) {
                    for(TTNativeExpressAd ad : adList) {
                        ad.setExpressInteractionListener(new TTNativeExpressAd.ExpressAdInteractionListener() {
                            //广告点击回调
                            @Override
                            public void onAdClicked(View view, int type) { }

                            //广告展示回调
                            @Override
                            public void onAdShow(View view, int type) { }

                            //广告渲染失败回调
                            @Override
                            public void onRenderFail(View view, String msg, int code) {
                                Log.d(LogTag, String.format("信息流广告渲染失败，错误代码: %s ，错误说明：%s", code, msg));
                            }

                            //广告渲染成功回调
                            @Override
                            public void onRenderSuccess(View view, float width, float height) {
                                advertDockingDevice.insert(view);
                            }
                        });
                        ad.render();
                        // 不喜欢样式设置
                        bindDislike(advertDockingDevice, ad, false);
                    }
                }
            }
        });
    }

    @Override
    public void injectionSplash(AdvertDockingDevice advertDockingDevice, boolean disableCountdown, int timeOut) {
        TTAdNative mTTAdNative = adManager().createAdNative(advertDockingDevice.getContext());
        /**
         * 开屏广告： 开屏广告为用户在进入App时展示的全屏广告。开屏广告为一个View，宽高默认为match_parent。
         * 注意： 开屏广告view：width =屏幕宽；height需要>=75%屏幕高 ，否则会影响计费。
         * 为了收益最大化所有开屏广告均要实时请求，不可缓存。
         *
         * 支持的广告尺寸：开屏广告的尺寸为开发者请求广告时设置的尺寸，建议设置的请求尺寸传入与展示区域大小保持一致，避免素材变形。
         *
         * 备注：模板渲染开屏和SDK渲染开屏的广告在请求方式上没有差异，仅是请求参数上的差异。
         * 模板渲染的开屏请求方法中需要设置setExpressViewAcceptedSize参数，SDK渲染开屏广告不需要设置此参数
         */

        // SDK渲染开屏广告： 默认提供
        AdSlot adSlot = new AdSlot.Builder()
                .setCodeId(advertCode)                      // 平台创建的代码位ID 以8开头9位数字
                .setSupportDeepLink(true)
                .setImageAcceptedSize(advertWidth, advertHeight) // 设置请求非模板广告素材的尺寸。 单位：px SDK渲染必需要设置且不为0：1080, 1920
                .build();

//        // 模板渲染开屏广告： 白名单，需要申请开通
//        AdSlot adSlot = new AdSlot.Builder()
//                .setCodeId(codeId)                    // 平台创建的代码位ID 以8开头9位数字
//                //模板广告需要设置期望个性化模板广告的大小,单位dp,代码位是否属于个性化模板广告，请在穿山甲平台查看。且仅是模板渲染的代码位ID使用，非模板渲染代码位切勿使用
//                .setExpressViewAcceptedSize(advertWidth, advertHeight)
//                .build();

        // adslot为请求广告的信息，SplashAdListener为广告加载成功或失败的回调，timeOut为加载开屏广告允许的最长时间（注：建议timeOut > 3500ms）
        mTTAdNative.loadSplashAd(adSlot, new TTAdNative.SplashAdListener() {
            //请求广告失败
            @Override
            @MainThread
            public void onError(int code, String message) {
                //开发者处理跳转到APP主页面逻辑
                Log.d(LogTag, String.format("开屏广告加载失败，错误代码: %s ，错误说明：%s", code, message));
                PangolinAdvert.this.onError();
            }

            //请求广告超时
            @Override
            @MainThread
            public void onTimeout() {
                //开发者处理跳转到APP主页面逻辑
                PangolinAdvert.this.onTimeout();
            }

            //请求广告成功
            @Override
            @MainThread
            public void onSplashAdLoad(TTSplashAd ad) {
                onLoaded();

                if (ad == null) {
                    return;
                }

                // 调用ad.getSplashView()获取到SplashView
                View splashView = ad.getSplashView();
                if(splashView != null && advertDockingDevice != null) {
                    ad.setSplashInteractionListener(new TTSplashAd.AdInteractionListener() {
                        //点击回调
                        @Override
                        public void onAdClicked(View view, int type) { }

                        //展示回调
                        @Override
                        public void onAdShow(View view, int type) { }

                        //跳过回调
                        @Override
                        public void onAdSkip() {
                            //开发者处理跳转到APP主页面逻辑
                        }

                        //超时倒计时结束
                        @Override
                        public void onAdTimeOver() {
                            //开发者处理跳转到APP主页面逻辑
                        }
                    });

                    advertDockingDevice.removeAll();
                    // 把SplashView 添加到ViewGroup中,注意开屏广告view：width =屏幕宽；height >=75%屏幕高
                    advertDockingDevice.insert(splashView);
                    // 设置不开启开屏广告倒计时功能以及不显示跳过按钮,如果这么设置，您需要自定义倒计时逻辑
                    if(disableCountdown) {
                        ad.setNotAllowSdkCountdown();
                    }
                } else {
                    //开发者处理跳转到APP主页面逻辑
                }
            }
        }, timeOut);

    }

    /**
     * 不喜欢原因弹窗
     */
    private final static class DislikeDialog extends TTDislikeDialogAbstract {
        final List<FilterWord> mList;
        private OnDislikeItemClick mOnDislikeItemClick;

        public DislikeDialog(@NonNull Context context, List<FilterWord> list) {
            super(context);

            this.mList = initData(list);
        }

        public void setOnDislikeItemClick(OnDislikeItemClick onDislikeItemClick) {
            mOnDislikeItemClick = onDislikeItemClick;
        }

        private List<FilterWord> initData(List<FilterWord> list) {
            //目前网盟的负反馈存在二级选项，需要特殊处理
            List<FilterWord> data = new ArrayList<>();
            if (list != null) {
                for (FilterWord filterWord : list) {
                    if (filterWord.hasSecondOptions()) {
                        data.addAll(initData(filterWord.getOptions()));
                    } else {
                        data.add(filterWord);
                    }
                }
            }
            return data;
        }

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            TTDislikeListView listView = findViewById(R.id.pangolinDislikeItemListView);
            listView.setAdapter(new MyDislikeAdapter());
            listView.setOnItemClickListener((parent, view, position, id) -> {
                DislikeDialog.this.dismiss();

                if (mOnDislikeItemClick != null) {
                    FilterWord word = null;
                    try {
                        word = (FilterWord) parent.getAdapter().getItem(position);
                    } catch (Throwable ignore) {
                    }
                    mOnDislikeItemClick.onItemClick(word);
                }
            });
        }

        @Override
        public int getLayoutId() {
            return R.layout.dialog_pangolin_dislike_custom;
        }

        @Override
        public int[] getTTDislikeListViewIds() {
            return new int[]{R.id.pangolinDislikeItemListView};
        }

        @Override
        public int[] getPersonalizationPromptIds() {
            return new int[0];
        }

        @Override
        public ViewGroup.LayoutParams getLayoutParams() {
            return null;
        }

        class MyDislikeAdapter extends BaseAdapter {

            @Override
            public int getCount() {
                return mList == null ? 0 : mList.size();
            }

            @Override
            public Object getItem(int position) {
                return mList == null ? null : mList.get(position);
            }

            @Override
            public long getItemId(int position) {
                return position;
            }

            @Override
            public View getView(int position, View convertView, ViewGroup parent) {
                FilterWord word = (FilterWord) getItem(position);

                TextView textView = new TextView(getContext());
                textView.setPadding(40, 40, 40, 40);
                textView.setTextColor(Color.BLACK);
                textView.setGravity(Gravity.CENTER);
                textView.setText(word.getName());

                return textView;
            }
        }

        public interface OnDislikeItemClick {
            void onItemClick(FilterWord filterWord);
        }
    }

}
